/*

	Copyright 2010 Etay Meiri

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef MATH_3D_H
#define	MATH_3D_H
#include "TriangleRice.h"
//#define _USE_MATH_DEFINES 
//#include <math.h>
#include <stdio.h>
#ifdef WIN32
#define _USE_MATH_DEFINES 
#include <cmath>
#else
#include <math.h>
#endif

#include <vector3.h>
#include <matrix3x3.h>
#include <matrix4x4.h>
#define ToRadian(x) (float)(((x) * M_PI / 180.0f))
#define ToDegree(x) (float)(((x) * 180.0f / M_PI))
//////////////////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////////////////////////
float RandomFloat();
struct Vector3f
{
	float x;
	float y;
	float z;

	Vector3f()
	{
	}

	Vector3f(float _x, float _y, float _z)
	{
		x = _x;
		y = _y;
		z = _z;
	}

	Vector3f& operator+=(const Vector3f& r)
	{
		x += r.x;
		y += r.y;
		z += r.z;

		return *this;
	}

	Vector3f& operator-=(const Vector3f& r)
	{
		x -= r.x;
		y -= r.y;
		z -= r.z;

		return *this;
	}

	Vector3f& operator*=(float f)
	{
		x *= f;
		y *= f;
		z *= f;

		return *this;
	}

	Vector3f Cross(const Vector3f& v) const;

	Vector3f& Normalize();

	void Rotate(float Angle, const Vector3f& Axis);

	void Print() const
	{
		printf("(%.02f, %.02f, %.02f)", x, y, z);
	}
};
inline Vector3f operator+(const Vector3f& l, const Vector3f& r)
{
	Vector3f Ret(l.x + r.x,
		l.y + r.y,
		l.z + r.z);

	return Ret;
}
inline Vector3f operator-(const Vector3f& l, const Vector3f& r)
{
	Vector3f Ret(l.x - r.x,
		l.y - r.y,
		l.z - r.z);

	return Ret;
}
inline Vector3f operator*(const Vector3f& l, float f)
{
	Vector3f Ret(l.x * f,
		l.y * f,
		l.z * f);

	return Ret;
}
struct Vector2i
{
	int x;
	int y;
};
struct Vector2f
{
	float x;
	float y;

	Vector2f()
	{
	}

	Vector2f(float _x, float _y)
	{
		x = _x;
		y = _y;
	}
};
struct Vector4f
{
	float x;
	float y;
	float z;
	float w;

	Vector4f()
	{        
	}

	Vector4f(float _x, float _y, float _z, float _w)
	{
		x = _x;
		y = _y;
		z = _z;
		w = _w;
	}

	void Print() const
	{
		printf("(%.02f, %.02f, %.02f, %.02f)", x, y, z, w);
	}       
};
struct PersProjInfo
{
	float FOV;
	float Width; 
	float Height;
	float zNear;
	float zFar;
};
class Matrix4f
{
public:
	float m[4][4];

	Matrix4f()
	{        
	}

	// constructor from Assimp matrix
	Matrix4f(const aiMatrix4x4& AssimpMatrix)
	{
		m[0][0] = AssimpMatrix.a1; m[0][1] = AssimpMatrix.a2; m[0][2] = AssimpMatrix.a3; m[0][3] = AssimpMatrix.a4;
		m[1][0] = AssimpMatrix.b1; m[1][1] = AssimpMatrix.b2; m[1][2] = AssimpMatrix.b3; m[1][3] = AssimpMatrix.b4;
		m[2][0] = AssimpMatrix.c1; m[2][1] = AssimpMatrix.c2; m[2][2] = AssimpMatrix.c3; m[2][3] = AssimpMatrix.c4;
		m[3][0] = AssimpMatrix.d1; m[3][1] = AssimpMatrix.d2; m[3][2] = AssimpMatrix.d3; m[3][3] = AssimpMatrix.d4;
	}

	Matrix4f(const aiMatrix3x3& AssimpMatrix)
	{
		m[0][0] = AssimpMatrix.a1; m[0][1] = AssimpMatrix.a2; m[0][2] = AssimpMatrix.a3; m[0][3] = 0.0f;
		m[1][0] = AssimpMatrix.b1; m[1][1] = AssimpMatrix.b2; m[1][2] = AssimpMatrix.b3; m[1][3] = 0.0f;
		m[2][0] = AssimpMatrix.c1; m[2][1] = AssimpMatrix.c2; m[2][2] = AssimpMatrix.c3; m[2][3] = 0.0f;
		m[3][0] = 0.0f           ; m[3][1] = 0.0f           ; m[3][2] = 0.0f           ; m[3][3] = 1.0f;
	}   

	Matrix4f(float a00, float a01, float a02, float a03,
		float a10, float a11, float a12, float a13,
		float a20, float a21, float a22, float a23,
		float a30, float a31, float a32, float a33)
	{
		m[0][0] = a00; m[0][1] = a01; m[0][2] = a02; m[0][3] = a03;
		m[1][0] = a10; m[1][1] = a11; m[1][2] = a12; m[1][3] = a13;
		m[2][0] = a20; m[2][1] = a21; m[2][2] = a22; m[2][3] = a23;
		m[3][0] = a30; m[3][1] = a31; m[3][2] = a32; m[3][3] = a33;        
	}

	void SetZero()
	{
		ZERO_MEM(m);
	}

	Matrix4f Transpose() const
	{
		Matrix4f n;

		for (unsigned int i = 0 ; i < 4 ; i++) {
			for (unsigned int j = 0 ; j < 4 ; j++) {
				n.m[i][j] = m[j][i];
			}
		}

		return n;
	}


	inline void InitIdentity()
	{
		m[0][0] = 1.0f; m[0][1] = 0.0f; m[0][2] = 0.0f; m[0][3] = 0.0f;
		m[1][0] = 0.0f; m[1][1] = 1.0f; m[1][2] = 0.0f; m[1][3] = 0.0f;
		m[2][0] = 0.0f; m[2][1] = 0.0f; m[2][2] = 1.0f; m[2][3] = 0.0f;
		m[3][0] = 0.0f; m[3][1] = 0.0f; m[3][2] = 0.0f; m[3][3] = 1.0f;
	}

	inline Matrix4f operator*(const Matrix4f& Right) const
	{
		Matrix4f Ret;

		for (unsigned int i = 0 ; i < 4 ; i++) {
			for (unsigned int j = 0 ; j < 4 ; j++) {
				Ret.m[i][j] = m[i][0] * Right.m[0][j] +
					m[i][1] * Right.m[1][j] +
					m[i][2] * Right.m[2][j] +
					m[i][3] * Right.m[3][j];
			}
		}

		return Ret;
	}

	Vector4f operator*(const Vector4f& v) const
	{
		Vector4f r;

		r.x = m[0][0]* v.x + m[0][1]* v.y + m[0][2]* v.z + m[0][3]* v.w;
		r.y = m[1][0]* v.x + m[1][1]* v.y + m[1][2]* v.z + m[1][3]* v.w;
		r.z = m[2][0]* v.x + m[2][1]* v.y + m[2][2]* v.z + m[2][3]* v.w;
		r.w = m[3][0]* v.x + m[3][1]* v.y + m[3][2]* v.z + m[3][3]* v.w;

		return r;
	}

	void Print() const
	{
		for (int i = 0 ; i < 4 ; i++) {
			printf("%f %f %f %f\n", m[i][0], m[i][1], m[i][2], m[i][3]);
		}       
	}

	float Determinant() const;

	Matrix4f& Inverse();

	void InitScaleTransform(float ScaleX, float ScaleY, float ScaleZ);
	void InitRotateTransform(float RotateX, float RotateY, float RotateZ);
	void InitTranslationTransform(float x, float y, float z);
	void InitCameraTransform(const Vector3f& Target, const Vector3f& Up);
	void InitPersProjTransform(const PersProjInfo& p);
};


struct Quaternion
{
	float x, y, z, w;

	Quaternion(float _x, float _y, float _z, float _w);

	void Normalize();

	Quaternion Conjugate();  
};

Quaternion operator*(const Quaternion& l, const Quaternion& r);

Quaternion operator*(const Quaternion& q, const Vector3f& v);


struct  Point
{
	float x;
	float y;
	Point(){}
	Point(float x, float y):x(x),y(y){}
	bool Point::equals(const Point& target) const
	{
		return ((x == target.x) && (y == target.y));
	}
	Point operator-(const Point& pt) const
	{
		return Point(x - pt.x, y - pt.y);
	}
};
const Point PointZero(0,0);
const Point PointOne(1,1);
struct  Size
{
	float width;
	float height;
	Size(){}
	Size(float width, float height):width(width),height(height){}
};
const Size SizeZero = Size(0,0);
const Size SizeOne = Size(1,1);
struct  Rect
{
	Point origin;
	Size  size;

	Rect(float x, float y, float width, float height)
	{
		origin.x = x;
		origin.y = y;

		size.width = width;
		size.height = height;
	}
	float getMaxX() const
	{
		return (float)(origin.x + size.width);
	}

	float getMidX() const
	{
		return (float)(origin.x + size.width / 2.0);
	}

	float getMinX() const
	{
		return origin.x;
	}

	float getMaxY() const
	{
		return origin.y + size.height;
	}

	float getMidY() const
	{
		return (float)(origin.y + size.height / 2.0);
	}

	float getMinY() const
	{
		return origin.y;
	}
	bool containsPoint(const Point& point) const
	{
		bool bRet = false;

		if (point.x >= getMinX() && point.x <= getMaxX()
			&& point.y >= getMinY() && point.y <= getMaxY())
		{
			bRet = true;
		}

		return bRet;
	}

	bool intersectsRect(const Rect& rect) const
	{
		return !(     getMaxX() < rect.getMinX() ||
			rect.getMaxX() <      getMinX() ||
			getMaxY() < rect.getMinY() ||
			rect.getMaxY() <      getMinY());
	}
	void print()
	{
		printf("%f,%f:%f,%f\n",origin.x,origin.y,size.width,size.height);
	}
};
const Rect RectZero = Rect(0,0,0,0);

struct AffineTransform {
	float a, b, c, d;
	float tx, ty;
	AffineTransform(){}
	AffineTransform(float a,float b,float c,float d,float tx,float ty):a(a),b(b),c(c),d(d),tx(tx),ty(ty){}
	void print()
	{
		printf("%f,%f,%f,%f,%f,%f\n",a,b,c,d,tx,ty);
	}
};

AffineTransform AffineTransformMake(float a, float b, float c, float d, float tx, float ty);
Point PointApplyAffineTransform(const Point& point, const AffineTransform& t);
Size SizeApplyAffineTransform(const Size& size, const AffineTransform& t);
AffineTransform AffineTransformMakeIdentity();
Rect RectApplyAffineTransform(const Rect& rect, const AffineTransform& anAffineTransform);
AffineTransform AffineTransformTranslate(const AffineTransform& t, float tx, float ty);
AffineTransform AffineTransformRotate(const AffineTransform& aTransform, float anAngle);
AffineTransform AffineTransformScale(const AffineTransform& t, float sx, float sy);
AffineTransform AffineTransformConcat(const AffineTransform& t1, const AffineTransform& t2);
bool AffineTransformEqualToTransform(const AffineTransform& t1, const AffineTransform& t2);
AffineTransform AffineTransformInvert(const AffineTransform& t);
void AffineToGL2(const AffineTransform *t, Matrix4f *m);

struct Tex2F
{
	float u,v;
	Tex2F(){}
	Tex2F(const float u, const float v):u(u),v(v){}
};
typedef unsigned char Colorbyte;
struct Color4B
{
	Colorbyte r;
	Colorbyte g;
	Colorbyte b;
	Colorbyte a;
	Color4B(){}
	Color4B(const Colorbyte r, const Colorbyte g, const Colorbyte b, const Colorbyte a):r(r),g(g),b(b),a(a){}

};
const Color4B ColorWhite(255,255,255,255);
struct V3F_C4B_T2F
{
	Vector3f        vertices;            
	Color4B        colors;               
	Tex2F            texCoords;    
	V3F_C4B_T2F(){}
} ;
struct V3F_C4B_T2F_Quad
{
	//left top 
	V3F_C4B_T2F    lt;
	//left bottom 
	V3F_C4B_T2F    lb;
	//right top
	V3F_C4B_T2F    rt;
	//right bottom
	V3F_C4B_T2F    rb;
	unsigned int tex;
	V3F_C4B_T2F_Quad(){}
} ;
#endif	/* MATH_3D_H */

